package JET;

import JET.CenterPanels.CPScene;
import JET.Centers.ControlCenter;
import JET.Centers.Scripts.ScriptsCenter;
import JET.MouseStuff.MouseMode;
import JET.MouseStuff.MouseSubMode;
import JET.geom.Vec2d;
import JET.graphics.GraphicPanel;

import JET.physics.RSpring;
import JET.physics.SBCollision;
import JET.physics.Spring;
import JET.physics.bodies.RigidBody;
import JET.physics.bodies.SpringBody;
import JET.physics.engine.JETContext;
import JET.physics.engine.JETEngine;
import JET.physics.steering.Torque;
import java.awt.Component;
import java.util.Collection;

/**
 *
 * @author 057Ry
 */
public final class Animator extends Thread implements Runnable
{
    static final ControlCenter CC = ControlCenter.getInstance();

    public final JETContext context = new JETContext();
    private GraphicPanel graphicPanel;
    private Scene scene;

    private boolean stayAlive = true;

    public Animator(String sceneName) {
        // Set animator scene
        this.scene = CC.getScene(sceneName);

        // Set animator graphic panel
        CPScene sceneTab = (CPScene)CC.GUIC.getSceneTab(sceneName);
        this.graphicPanel = sceneTab.getGraphicPanel();

        context.appendJETContext(scene.getJETContext());
        Collection<SceneItem> siCol = scene.getSceneItemsListSync();
        synchronized(siCol) {
            for(SceneItem si : siCol)
                context.appendJETContext(si.getEngine());
        }

        context.setValuesAndConstantsByJETContext(scene.getJETContext());
        context.setMaterialsByJETContext(scene.getJETContext());
    }

    /**
     * Stops simulation.
     * Animator is useless after call to this method.
     */
    public void stopSimulation() {
        stayAlive = false;
    }

    @Override
    public void run()
    {
        int recSleep = 0;
        while( stayAlive ) {
            try
            {
                // Main Loop Stuff
                for (int i=0; i<CC.getMainLoopNoRedrawSteps(); i++)
                {
                    Collection<SpringBody> sbCol = context.getSpringBodyListSync();
                    synchronized(sbCol) {for(SpringBody sb : sbCol) sb.zeroForces();}

                    Collection<RigidBody> rbCol = context.getRigidBodyListSync();
                    synchronized(rbCol) {for(RigidBody rb : rbCol) {rb.zeroForces();}}

                    Collection<Spring> springCol = context.getSpringListSync();
                    synchronized(springCol) {
                        for(Spring s : springCol)
                            s.doYourJob();
                    }

                    Collection<RSpring> rSpringCol = context.getRSpringListSync();
                    synchronized(rSpringCol) {
                        for(RSpring rs : rSpringCol)
                            rs.doYourJob();
                    }

                    Collection<Torque> torqueCol = context.getTorqueListSync();
                    synchronized(torqueCol) {
                        for(Torque t : torqueCol)
                            if (t.isActive()) t.doYourJob();
                    }

                    Vec2d grav = new Vec2d(0.,-context.gravity);
                    synchronized(sbCol) {
                        for(SpringBody sb : sbCol) {
                            if (context.isApplyGravity()) sb.accumulateExternalForces(grav);
                            sb.accumulateInternatForces();
                        }
                    }

                    synchronized(rbCol) {
                        for(RigidBody rb : rbCol) {
                            if (context.isApplyGravity()) rb.applyForce(grav, rb.position);
                            rb.accumulateInternatForces();
                        }
                    }

                    scene.SC.notifyOnStep();

                    ////////////////////////////////////////////////////////////
                    //                      D O   S T E P                     //
                    ////////////////////////////////////////////////////////////
                    
                    JETEngine.performSimulationStep(context);

                    if (context.isFindCollisions())
                        JETEngine.checkCollisions(context);

                    if (context.isResolveCollisions())
                        JETEngine.handleCollisions(context);

                    if (context.isApplyDamping()) {
                        synchronized(sbCol) {
                            for(SpringBody sb : sbCol)
                                sb.dampVelocities(context.damping);
                        }
                        synchronized(rbCol) {
                            for(RigidBody rb : rbCol)
                                rb.damp(context.damping);
                        }
                    }

//                    // Recording
//                    recSleep++;
//                    if (CC.isRecord() && CC.getRecorder()!=null && recSleep>30) {
//                        for(SpringBody sb : sbCol)
//                            CC.getRecorder().addSpringBodyData(sb, context.getTime());
//
//                        recSleep = 0;
//                    }
                }

//                // Notify on body collisions
//                ScriptsCenter SC = scene.SC;
//                for(int i=0; i<context.sbColInfoCount; i++) {
//                    SBCollision col = context.sbColInfoList.get(i);
//                    SC.notifyOnBodyCollision(col, i);
//                }

//                scene.SC.notifyOnPrePaint();

                // Redrawing
                //scene.GC.repaintAllStuff();
                ((Component)graphicPanel).repaint();

//                scene.SC.notifyOnPostPaint();

                if (CC.GUIC.recorderDialog.isVisible())
                    CC.GUIC.recorderDialog.repaint();

                // Follow camera stuff
                if (scene.EC.isFollowCamera() && scene.EC.currentBody!=null &&
                    !(CC.MC.getMouseMode()==MouseMode.MOVE_MASSES && CC.MC.getMouseSubMode()==MouseSubMode.FIND_SECOND_OBJECT) ) {
                    if (scene.EC.currentBody instanceof SpringBody)
                        scene.GC.getGraphicPanel().centerViewToVec(((SpringBody)scene.EC.currentBody).getCenterVec());
                    else if (scene.EC.currentBody instanceof RigidBody)
                        scene.GC.getGraphicPanel().centerViewToVec(((RigidBody)scene.EC.currentBody).position);
                }

                Thread.sleep(CC.getMainLoopSleepTime());
            }
            catch (InterruptedException ex){ return; }
            catch (Exception ex){ ex.printStackTrace(); }
        }
   }
}
